/*  ----------------------------------------------------------------------------
 *  Arello Mobile
 *  Fast 3d Engine
 *  ----------------------------------------------------------------------------
 * Except where otherwise noted, this work is licensed under
 * http://creativecommons.org/licenses/by/3.0
 */

#include <win32/win32.h>
#include "inproc_server.h"

//inproc com server requires these functions to be in dll
extern "C" BOOL __stdcall DllMain(HINSTANCE module_handle, DWORD reason, void *);
extern "C" long __stdcall DllRegisterServer();
extern "C" long __stdcall DllUnregisterServer();
extern "C" long __stdcall DllGetClassObject(const GUID & coclass_iid, const GUID & class_factory_iid, void ** ptr);
extern "C" long __stdcall DllCanUnloadNow();

//these statics are used in DllMain(), DllGetClassObject() and DllCanUnloadNow()
static libcom::InprocServer * inproc_server_ptr = 0;
String libcom::InprocServer::server_dll_path("");

BOOL libcom::InprocServer::DllMain(HINSTANCE module_handle, DWORD reason)
{
	switch(reason)
	{
	case DLL_PROCESS_ATTACH:
	{
		char path[1024];
		GetModuleFileName(module_handle, path, 1024);
		libcom::InprocServer::server_dll_path = String(path);
		inproc_server_ptr = new libcom::InprocServer();
		libcom::create_class_factories(inproc_server_ptr);
		return 1;
	}
	break;
	
	case DLL_PROCESS_DETACH:
	{
		delete inproc_server_ptr; inproc_server_ptr = 0;
	}
	break;
	}
	
	return 0;
}



extern "C" long __stdcall DllRegisterServer()
{
	if(!libcom::InprocServer::register_typelib_resource())
	{
		//return 1;
	}

	libcom::register_class_factories(inproc_server_ptr->server_dll_path);
		
	return 0;
}



extern "C" long __stdcall DllUnregisterServer()
{
	return 0;
}



extern "C" long __stdcall DllGetClassObject(const GUID & coclass_iid, const GUID & class_factory_iid, void ** ptr)
{
	return inproc_server_ptr->get_factory(coclass_iid, class_factory_iid, ptr);
}



extern "C" long __stdcall DllCanUnloadNow()
{
	if(!inproc_server_ptr)
		return S_OK;
		
	return inproc_server_ptr->locked() ? S_FALSE : S_OK;
}



namespace libcom
{

InprocServer::InprocServer()
{
}


InprocServer::~InprocServer()
{
	//Release all classfactories
	for(Factories::iterator i = factories.begin(); i != factories.end(); ++i)
		i->second->Release();
}



long InprocServer::get_factory(const GUID & coclass, const GUID & factory_iid, void ** interface_ptr)
{
	for(Factories::iterator i = factories.begin(); i != factories.end(); ++i)
	{
		if(i->first == coclass && S_OK == i->second->QueryInterface(factory_iid, interface_ptr))
			return S_OK;
	}
	
	return CLASS_E_CLASSNOTAVAILABLE;
}



bool InprocServer::register_typelib_resource()
{
	//if anybody knows how to use converter to convert String to WString,
	//rewrite this code ;)
	const int size = server_dll_path.size();
	wchar_t * buf = new wchar_t[size + 1];
	MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS, server_dll_path.c_str(), size, buf, size);
	buf[size] = L'\0';
	WString w_module_path(buf);
	delete[]buf;
	
	//register type library resource
	ITypeLib * type_library;
	if(S_OK != LoadTypeLib(w_module_path.c_str(), &type_library))
	{
		return false;
	}

	if(S_OK != RegisterTypeLib(type_library, const_cast<wchar_t*>(w_module_path.c_str()), (0)))
		return false;

	type_library->Release();

	return true;
}

}//end of namespace

